﻿using Newtonsoft.Json.Linq;

namespace WebChatApi
{
    /// <summary>
    /// Extensions for JSON
    /// </summary>
    public static class JsonExtensions
    {
        /// <summary>
        /// Gets the JSON object by specific x-path.
        /// </summary>
        /// <param name="jObject">The JSON object in <see cref="JObject" /> type.</param>
        /// <param name="xPath">The x-path.</param>
        /// <returns>The matched <see cref="JToken" /> instance. If not found, return null.</returns>
        /// <example>
        /// Samples below show you how to <see cref="XPath" /> method and the expected result.
        ///   <code>
        /// string json = @"{Property1: {Array:['item1','item2','item3'],Count:3}, Property2: 'hello'}".Replace('\'', '"');
        /// var obj = JToken.Parse(json);   //Parse JSON object from string.
        /// obj = obj.XPath("Property1/Array"); //obj = {Array:['item1','item2','item3']}
        /// var result = obj.XPath("/Property1/Count"); // obj = {Count:3}
        /// var result2 = obj.XPath("/Property1/Array[2]"); //obj = "item3"
        ///   </code>
        /// Note that, if the xPath starts "/", it means to be from the root node, otherwise, from current node.
        ///   </example>
        private static JToken GetJTokenByXPath(this JToken jObject, string xPath)
        {
            JToken result = null;

            if (jObject != null && !string.IsNullOrWhiteSpace(xPath))
            {
                xPath = xPath.Trim();
                int index = xPath.IndexOf('/');

                if (index < 0)
                {
                    result = jObject.SelectToken(xPath);
                }
                else if (index == 0)
                {
                    result = GetJTokenByXPath(jObject.Root, xPath.Substring(1));
                }
                else if (index == (xPath.Length - 1))
                {
                    result = GetJTokenByXPath(jObject, xPath.Substring(0, index));
                }
                else
                {
                    result = GetJTokenByXPath(jObject.SelectToken(xPath.Substring(0, index)), xPath.Substring(index + 1));
                }
            }

            return result;
        }

        /// <summary>
        /// Gets the JSON object by specific x-path.
        /// </summary>
        /// <param name="jObject">The JSON object in <see cref="JObject" /> type.</param>
        /// <param name="xPath">The x-path.</param>
        /// <returns>The matched <see cref="JToken" /> instance. If not found, return null.</returns>
        /// <example>
        /// Samples below show you how to <see cref="XPath" /> method and the expected result.
        ///   <code>
        /// string json = @"{Property1: {Array:['item1','item2','item3'],Count:3}, Property2: 'hello'}".Replace('\'', '"');
        /// var obj = JToken.Parse(json);   //Parse JSON object from string.
        /// obj = obj.XPath("Property1/Array"); //obj = {Array:['item1','item2','item3']}
        /// var result = obj.XPath("/Property1/Count"); // obj = {Count:3}
        /// var result2 = obj.XPath("/Property1/Array[2]"); //obj = "item3"
        ///   </code>
        /// Note that, if the xPath starts "/", it means to be from the root node, otherwise, from current node.
        ///   </example>
        public static JToken XPath(this JObject jObject, string xPath)
        {
            return GetJTokenByXPath(jObject, xPath);
        }

        /// <summary>
        /// Gets the JSON object by specific x-path.
        /// </summary>
        /// <param name="jObject">The JSON object in <see cref="JToken" /> type.</param>
        /// <param name="xPath">The x-path.</param>
        /// <returns>The matched <see cref="JToken" /> instance. If not found, return null.</returns>
        /// <example>
        /// Samples below show you how to <see cref="XPath" /> method and the expected result.
        ///   <code>
        /// string json = @"{Property1: {Array:['item1','item2','item3'],Count:3}, Property2: 'hello'}".Replace('\'', '"');
        /// var obj = JToken.Parse(json);   //Parse JSON object from string.
        /// obj = obj.XPath("Property1/Array"); //obj = {Array:['item1','item2','item3']}
        /// var result = obj.XPath("/Property1/Count"); // obj = {Count:3}
        /// var result2 = obj.XPath("/Property1/Array[2]"); //obj = "item3"
        ///   </code>
        /// Note that, if the xPath starts "/", it means to be from the root node, otherwise, from current node.
        ///   </example>
        public static JToken XPath(this JToken jObject, string xPath)
        {
            return GetJTokenByXPath(jObject, xPath);
        }
    }
}
